package com.huawei.cloud.phone.management.application.connection;

import com.huawei.cloud.phone.management.BaseTest;
import com.huawei.cloud.phone.management.Constants;
import com.huawei.cloud.phone.management.application.connection.dto.EventDTO;
import com.huawei.cloud.phone.management.application.connection.dto.EventInfoDTO;
import com.huawei.cloud.phone.management.domain.cache.CacheResource;
import com.huawei.cloud.phone.management.domain.connection.Connection;
import com.huawei.cloud.phone.management.domain.connection.ConnectionGateway;
import com.huawei.cloud.phone.management.domain.connection.ConnectionStatus;
import com.huawei.cloud.phone.management.domain.phone.Phone;
import com.huawei.cloud.phone.management.domain.connection.ConnectionParam;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;

import java.util.Optional;

public class ConnectionServiceTest extends BaseTest {
    @Autowired
    private ConnectionService connectionService;

    @MockBean
    private ConnectionGateway connectionGateway;

    @MockBean
    private CacheResource<String> cacheResource;

    @Test
    public void testReportEvent_logIn_success() {
        // mock login
        Mockito.when(cacheResource.getData(Constants.PHONE_ID)).thenReturn(Optional.of(Constants.SESSION_ID));
        mockConnection(ConnectionStatus.CONNECTED);

        // param
        EventInfoDTO eventInfoDTO = buildEventInfoDTO(0);

        // action
        connectionService.reportEvent(eventInfoDTO);

        // check
        Mockito.verify(cacheResource, Mockito.times(1)).refreshTimeout(Constants.PHONE_ID, 200);
    }

    @Test(expected = RuntimeException.class)
    public void testReportEvent_logIn_authFailed() {
        // mock no login
        Mockito.when(cacheResource.getData(Constants.SESSION_ID)).thenReturn(Optional.empty());

        // param
        EventInfoDTO eventInfoDTO = buildEventInfoDTO(0);

        // action
        connectionService.reportEvent(eventInfoDTO);
    }

    @Test
    public void testReportEvent_logOut_success() {
        // mock login
        ConnectionParam connectionParam = new ConnectionParam();
        Mockito.when(cacheResource.getData(Constants.PHONE_ID)).thenReturn(Optional.of(Constants.SESSION_ID));
        mockConnection(ConnectionStatus.CONNECTED);

        // param
        EventInfoDTO eventInfoDTO = buildEventInfoDTO(1);

        // action
        connectionService.reportEvent(eventInfoDTO);

        // check
        Mockito.verify(connectionGateway, Mockito.times(1)).updateStatus(Constants.CONNECTION_ID, ConnectionStatus.CONNECTED, ConnectionStatus.IDLE);
        Mockito.verify(cacheResource, Mockito.times(1)).clearData(Constants.PHONE_ID);
    }

    @Test(expected = RuntimeException.class)
    public void testReportEvent_logOut_authFailed() {
        // mock not login
        ConnectionParam connectionParam = new ConnectionParam();
        Mockito.when(cacheResource.getData(Constants.SESSION_ID)).thenReturn(Optional.empty());

        // param
        EventInfoDTO eventInfoDTO = buildEventInfoDTO(1);

        // action
        connectionService.reportEvent(eventInfoDTO);
    }

    private EventInfoDTO buildEventInfoDTO(int status) {
        EventDTO eventDTO = new EventDTO();
        eventDTO.setStatus(status);
        eventDTO.setSessionId(Constants.SESSION_ID);
        eventDTO.setPhoneId(Constants.PHONE_ID);
        EventInfoDTO eventInfoDTO = new EventInfoDTO();
        eventInfoDTO.setEventType("app");
        eventInfoDTO.setEvent(eventDTO);
        return eventInfoDTO;
    }

    @Test
    public void test_heartbeat_connect_success() {
        // mock login
        ConnectionParam connectionParam = new ConnectionParam();
        Mockito.when(cacheResource.getData(Constants.PHONE_ID)).thenReturn(Optional.of(Constants.SESSION_ID));
        mockConnection(ConnectionStatus.CONNECTED);

        // param
        EventInfoDTO eventInfoDTO = buildHeartbeatEventInfoDTO();
        eventInfoDTO.getEvent().setSessionId(Constants.SESSION_ID);

        // action
        connectionService.heartbeat(eventInfoDTO);

        // check
        Mockito.verify(connectionGateway, Mockito.times(1)).updateStatus(Constants.CONNECTION_ID, ConnectionStatus.CONNECTED, ConnectionStatus.CONNECTED);
        Mockito.verify(cacheResource, Mockito.times(1)).refreshTimeout(Constants.PHONE_ID, 200);
    }

    private EventInfoDTO buildHeartbeatEventInfoDTO() {
        EventDTO eventDTO = new EventDTO();
        eventDTO.setPhoneId(Constants.PHONE_ID);
        EventInfoDTO eventInfoDTO = new EventInfoDTO();
        eventInfoDTO.setEventType("heartbeat");
        eventInfoDTO.setEvent(eventDTO);
        return eventInfoDTO;
    }

    private void mockConnection(ConnectionStatus status) {
        Connection connection = new Connection();
        connection.setConnectionId(Constants.CONNECTION_ID);
        connection.setPhone(new Phone());
        connection.setStatus(status);
        Mockito.when(connectionGateway.getConnectionByPhoneId(Constants.PHONE_ID)).thenReturn(Optional.of(connection));
    }

    @Test
    public void test_heartbeat_disconnect_success() {
        // mock login
        ConnectionParam connectionParam = new ConnectionParam();
        Mockito.when(cacheResource.getData(Constants.PHONE_ID)).thenReturn(Optional.of(Constants.SESSION_ID));
        Mockito.doNothing().when(cacheResource).clearData(Constants.PHONE_ID);
        mockConnection(ConnectionStatus.CONNECTED);

        // param
        EventInfoDTO eventInfoDTO = buildHeartbeatEventInfoDTO();

        // action
        connectionService.heartbeat(eventInfoDTO);

        // check
        Mockito.verify(connectionGateway, Mockito.times(1)).updateStatus(Constants.CONNECTION_ID, ConnectionStatus.CONNECTED, ConnectionStatus.IDLE);
        Mockito.verify(cacheResource, Mockito.times(0)).refreshTimeout(Constants.PHONE_ID, 120);
        Mockito.verify(cacheResource, Mockito.times(1)).clearData(Constants.PHONE_ID);
    }
}